﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Map
{
    public class MapInfo
    {
        public Vector2Int Destination { get; set; }
        public int Width { get; }
        public int Height { get; }
        
        private CellType[,] CellTypes { get; }
        
        private int[,] TextureIDs { get; }

        private IDictionary<Vector2Int, int> PositionToResourceID { get; }

        private readonly int _rootX;


        public MapInfo(int width, int height, int rootX)
        {
            Width = width;
            Height = height;
            _rootX = rootX;
            
            CellTypes = new CellType[height, width];
            TextureIDs = new int[height, width];
            PositionToResourceID = new Dictionary<Vector2Int, int>();
        }

        public CellType GetCellType(int i, int j)
        {
            return CellTypes[i, j];
        }

        public void PutPath(Vector2Int position)
        {
            position = GetArrayPosition(position);
            
            CellTypes[position.x, position.y] = CellType.Path;
        }

        public void PutResource(Vector2Int position, int resourceID)
        {
            position = GetArrayPosition(position);
            
            CellTypes[position.x, position.y] = CellType.Resource;
            PositionToResourceID.Add(position, resourceID);
        }

        public bool TryGetResourceID(Vector2Int position, out int resourceID)
        {
            resourceID = 0;
            position = GetArrayPosition(position);

            return TryGetResourceID(position.x, position.y, out resourceID);
        }
        
        public bool TryGetResourceID(int i, int j, out int resourceID)
        {
            resourceID = 0;

            if (CellTypes[i, j] != CellType.Resource)
            {
                return false;
            }
            
            return PositionToResourceID.TryGetValue(new Vector2Int(i, j), out resourceID);
        }

        public int GetTextureID(int i, int j)
        {
            return TextureIDs[i, j];
        }

        public void GenerateObstacles()
        {
            const float obstacleMaxChance = 0.2f;
            const float obstacleFillStopMaxChance = 0.9f;
            const float obstacleFillStopMinChance = 0.6f;
            for (int i = 0; i < Height; i++)
            {
                // The deeper, the more chance of obstacle
                float obstacleChance = Mathf.Lerp(0f, obstacleMaxChance, i / (float)Height);
                float obstacleFillStopChance = Mathf.Lerp(obstacleFillStopMaxChance, obstacleFillStopMinChance, i / (float)Height);
                for (int j = 0; j < Width; j++)
                {
                    if (CellTypes[i, j] == CellType.None && Random.value < obstacleChance)
                    {
                        PutObstacle(i, j, obstacleFillStopChance);
                    }
                }
            }
        }

        public Vector3Int GetTilePosition(int i, int j)
        {
            return new Vector3Int(j - _rootX, -i - 1);
        }
        
        private void PutObstacle(int x, int y, float stopChance)
        {
            if (Random.value < stopChance)
            {
                return;
            }
            
            if (x < 0 || x >= Height || y < 0 || y >= Width || CellTypes[x, y] != CellType.None)
            {
                return;
            }
            
            CellTypes[x, y] = CellType.Obstacle;
            
            PutObstacle(x + 1, y, stopChance);
            PutObstacle(x - 1, y, stopChance);
            PutObstacle(x, y + 1, stopChance);
            PutObstacle(x, y - 1, stopChance);
        }

        private Vector2Int GetArrayPosition(Vector2Int position)
        {
            Vector2Int arrayPosition = new(-position.y - 1, position.x + _rootX);
            if (arrayPosition.x < 0 || arrayPosition.y < 0 || arrayPosition.x >= Height || arrayPosition.y >= Width)
            {
                Debug.LogError($"Position {position} is out of bound! Converted as: {arrayPosition}");
                return default;
            }
            return arrayPosition;
        }
    }
}